除了 Dart 自带的 io 包之外,人气较高的是 dio
库,其内部也是由 HttpClient
发起请求的,只不过做了一些封装,让我们使用起来更方便一些,它支持 Restful API
、FormData
、拦截器、请求取消、Cookie
管理、文件上传/下载、超时、自定义适配器等。
引入并创建实例
dependencies:
dio: ^3.0.9
然后在 dart 文件中添加依赖:
import 'package:dio/dio.dart';
接下来就是创建 dio 实例了:
Dio dio = Dio();
super easy~接下来就可以调用 dio 的一系列方法来进行我们的操作了:
get 请求
和 IO 包一样,dio 同样提供了两种方法来帮助我们完成 get 请求
get
Future<Response<T>> get<T>(
String path, {
Map<String, dynamic> queryParameters,
Options options,
CancelToken cancelToken,
ProgressCallback onReceiveProgress,
})
path,请求路径,需要
http://
或https://
开头,否则会抛异常。queryParameters,请求参数
dio.get("http://xx.xx.xx.xx:8080/xxxxx/SelectBillByUserId?userId=d969887a274347b98a0b99a830a2d6f");
等效于:
dio.get("http://xx.xx.xx.xx:8080/xxxxx/SelectBillByUserId,queryParameters: { "userId":"d969887a274347b98a0b99a830a2d6f", });
options,一个
Options
对象,用于描述http
请求信息和配置。 每个 Dio 实例都有一个针对自己发出的所有请求的基本配置(在创建 Dio 对象时设置),当发出单个请求时,我们可以使用Options
覆盖基本配置。Options({ String method, int sendTimeout, int receiveTimeout, Map<String, dynamic> extra, Map<String, dynamic> headers, ResponseType responseType, String contentType, ValidateStatus validateStatus, bool receiveDataWhenStatusError, bool followRedirects, int maxRedirects, RequestEncoder requestEncoder, ResponseDecoder responseDecoder, })
method,http 请求方法
sendTimeout,发送超时时间,单位是毫秒.
receiveTimeout,接收超时时间,单位是毫秒.
extra,用户自定义字段,可以在
Interceptor
、Transformer
和Response
中取到.headers,请求头
responseType,表示期望以那种格式(方式)接受响应数据。目前
ResponseType
接受三种类型JSON
,STREAM
,PLAIN
。默认值是
JSON
, 当响应头中content-type
为application/json
时,dio 会自动将响应内容转化为json
对象。 如果想以二进制方式接受响应数据,如下载一个二进制文件,那么可以使用STREAM
. 如果想以文本(字符串)格式接收响应数据,请使用PLAIN
.contentType,请求的
Content-Type
。默认值为application/json; charset=utf-8
。validateStatus,决定 http 响应状态码是否被 dio 视为请求成功, 返回
true
, 请求结果就会按成功处理,否则会按失败处理.receiveDataWhenStatusError,http 状态码失败时是否接收响应数据。
followRedirects,如果此请求应自动跟随重定向,则将此属性设置为 true 。 默认为 true 。
maxRedirects,将
followRedirects
属性设置为 true 时要遵循的最大重定向数。 如果超过此数字,则会添加一个错误事件,并带有一个RedirectException
。 默认值为 5。requestEncoder,默认请求编码器是
utf8encoder
,您可以通过此选项设置自定义编码器。responseDecoder,默认的响应解码器是
utf8decoder
,您可以通过此选项设置自定义解码器,它将在Transformer
中使用。
cancelToken,你可以通过 cancel token 来取消发起的请求:
注意: 同一个cancel token 可以用于多个请求,当一个cancel token取消时,所有使用该cancel token的请求都会被取消。
onReceiveProgress,接收进度
getUri
Future<Response<T>> getUri<T>(
Uri uri, {
Options options,
CancelToken cancelToken,
ProgressCallback onReceiveProgress,
})
没什么好讲的,具体属性参见 get 方法。
post 请求
同样是两种方法:
post
Future<Response<T>> post<T>(
String path, {
data,
Map<String, dynamic> queryParameters,
Options options,
CancelToken cancelToken,
ProgressCallback onSendProgress,
ProgressCallback onReceiveProgress,
})
- path,请求路径
- data,请求的数据
- queryParameters,同 get
- options,同 get
- cancelToken,同 get
- onSendProgress,发送数据进度
- onReceiveProgress,同 get
postUri
Future<Response<T>> postUri<T>(
Uri uri, {
data,
Options options,
CancelToken cancelToken,
ProgressCallback onSendProgress,
ProgressCallback onReceiveProgress,
})
不赘述
下载数据
dio 还提供了一些其他的 Restful API
,这些 API 都是 request
的别名。
Future get(...)
Future post(...)
Future put(...)
Future delete(...)
Future head(...)
Future put(...)
Future path(...)
Future download(...)
其他的不说了,download
来讲一下,dio 提供了下载请求:
Future<Response> download(
String urlPath, //请求路径
savePath, //保存路径
{
ProgressCallback onReceiveProgress, //接收进度
Map<String, dynamic> queryParameters, //请求参数
CancelToken cancelToken, //取消请求 token
bool deleteOnError = true, //发生错误时是否删除
String lengthHeader = Headers.contentLengthHeader, //消息头长度
data, //请求的数据
Options options, //请求选项,参见 get 方法
})
并发请求
dio 可以发起多个并发请求:
response = await Future.wait([dio.post("/info"), dio.get("/token")]);
响应
不管是 get 请求还是 post 请求,都会返回一个请求响应:
Response({
this.data,
this.headers,
this.request,
this.isRedirect,
this.statusCode,
this.statusMessage,
this.redirects,
this.extra,
})
- data,响应数据,可能已经被转换了类型, 详情请参考 Options 中的
ResponseType
. - headers,响应头
- request,本次请求信息
- isRedirect,是否重定向(Flutter Web不可用)
- statusCode,状态码
- statusMessage,状态信息
- redirects,重定向信息(Flutter Web不可用)
- extra,响应对象的自定义字段(可以在拦截器中设置它),调用方可以在
then
中获取.
拦截器
每个 Dio 实例都可以添加任意多个拦截器,他们组成一个队列,拦截器队列的执行顺序是 FIFO。通过拦截器你可以在请求之前或响应之后(但还没有被 then
或 catchError
处理)做一些统一的预处理操作。
void fetchAlbum() async {
Dio dio = Dio();
dio.interceptors.add(InterceptorsWrapper(
onRequest: (RequestOptions options) {
print("---------------onRequest------------------");
return options;
},
onResponse: (Response e) {
print("-------------onResponse-----------------");
return e;
},
onError: (DioError e) {
print("--------------------onError-------------");
return e;
}));
dio.interceptors.add(InterceptorsWrapper(
onRequest: (RequestOptions options) {
print("||||||||||||||||||||||||onRequest||||||||||||||||||||||||");
return options;
},
onResponse: (Response e) {
print("||||||||||||||||||||||||onResponse||||||||||||||||||||||||");
return e;
},
onError: (DioError e) {
print("||||||||||||||||||||||||onError||||||||||||||||||||||||");
return e;
}));
/// 这个请求因为是 http:///,所以会出错
Response response = await dio.get("http:///xxx.xxx.xxx.xxx:8080/LifeKeeper/SelectBillByUserId",queryParameters: {
"userId":"d969887a274347b98a0b99a830a2d6f4"
});
print(response.data.toString());
}
上面的代码执行结果如下:
I/flutter (26652): ---------------onRequest------------------
I/flutter (26652): ||||||||||||||||||||||||onRequest||||||||||||||||||||||||
I/flutter (26652): --------------------onError-------------
I/flutter (26652): ||||||||||||||||||||||||onError||||||||||||||||||||||||
E/flutter (26652): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: DioError [DioErrorType.DEFAULT]: Invalid argum......
我们设置了两个拦截器,从输出内容也可以看出来,执行顺序是 FIFO
锁定/解锁拦截器
你可以通过调用拦截器的 lock()/unlock
方法来锁定/解锁拦截器。一旦请求/响应拦截器被锁定,接下来的请求/响应将会在进入请求/响应拦截器之前排队等待,直到解锁后,这些入队的请求才会继续执行(进入拦截器)。这在一些需要串行化请求/响应的场景中非常实用,后面我们将给出一个示例。
tokenDio = Dio(); //Create a instance to request the token.
tokenDio.options = dio.options;
dio.interceptors.add(InterceptorsWrapper(
onRequest:(Options options) async {
// If no token, request token firstly and lock this interceptor
// to prevent other request enter this interceptor.
dio.interceptors.requestLock.lock();
// We use a Dio(to avoid dead lock) instance to request token.
Response response = await tokenDio.get("/token");
//Set the token to headers
options.headers["token"] = response.data["data"]["token"];
dio.interceptors.requestLock.unlock();
return options; //continue
}
));
清空等待队列
之前说过,我们可以为 dio 设置多个拦截器,在执行过程中,可以调用拦截器的clear()
方法来清空等待队列。
在拦截器中完成和终止请求/响应
在所有拦截器中,你都可以改变请求执行流, 如果你想完成请求/响应并返回自定义数据,你可以返回一个 Response
对象或返回 dio.resolve(data)
的结果。 如果你想终止(触发一个错误,上层catchError
会被调用)一个请求/响应,那么可以返回一个DioError
对象或返回 dio.reject(errMsg)
的结果.
dio.interceptors.add(InterceptorsWrapper(
onRequest:(RequestOptions options){
return dio.resolve("fake data")
},
));
Response response = await dio.get("/test");
print(response.data);//"fake data"